package restless.core {
	
	import flash.geom.Point;
	import restless.core.interfaces.ICheckColisions;
	import restless.core.interfaces.IColisionable;
	import restless.utilities.Benchmark;
	
	public class RLEColision implements ICheckColisions {
		
		protected var vColisionList:Vector.<IColisionable>;
		//protected var vColCheck:Vector.<IColisionable>;
		
		protected var _checks:Vector.<IColisionable>;
		private var _grid:Vector.<Vector.<IColisionable>>;
		private var _gridSize:Number;
		private var _height:Number;
		private var _numCells:int;
		private var _numCols:int;
		private var _numRows:int;
		private var _width:Number;
		private var _minHitBox:Number;
		private var _pause:Boolean = false;
		
		private var index1:int;
		private var index2:int;
		private var cellALength:int;
		private var cellBLength:int
		private var cellA:Vector.<IColisionable>;
		private var cellB:Vector.<IColisionable>;
		
		private var dx:Number;
		private var dy:Number;
		private var dist:Number;
		
		protected var bitmapDataColisionTest:Boolean;
		protected var colObj01:IColisionable;
		protected var colObj02:IColisionable;
		
		protected var id01:String;
		protected var id02:String;
		protected var colID01:int;
		protected var colID02:int;
		private var y2:Number;
		private var x2:Number;
		protected var _customColision:Boolean = false;
		protected var i:int;
		protected var tempPoint:Point = new Point;
		
		public function RLEColision(playAreaW:int, playAreaH:int, minGridSize:int, minHitBoxSize:int, pixelBased:Boolean = false, ...args) {
			vColisionList = new Vector.<IColisionable>;
			_width = playAreaW;
			_height = playAreaH;
			_gridSize = minGridSize;
			_minHitBox = minHitBoxSize;
			bitmapDataColisionTest = pixelBased;
			
			_numCols = Math.ceil(_width / _gridSize);
			_numRows = Math.ceil(_height / _gridSize);
			_numCells = _numCols * _numRows;
			
			_grid = new Vector.<Vector.<IColisionable>>;
			_checks = new Vector.<IColisionable>;
		} 
		
		public function addColItem(asset:IColisionable):void {
			vColisionList.push(asset);
		}
		
		public function removeColItem(asset:IColisionable):void { 
			i = vColisionList.indexOf(asset);
			vColisionList.splice(i, 1);
		}
		
		public function getObj(colID:int):IColisionable {
			i = vColisionList.length - 1;
			var tempNum:int = colID;
			for (i; i > -1; i--) {
				colObj01 = vColisionList[i];
				if (colObj01.colisionId == tempNum) {
					break;
				}
			}
			return colObj01; 
		}
		
		private function checkOneCell(x:int, y:int):void {
			cellA = _grid[y * _numCols + x];
			if(cellA == null) { return; }
			
			cellALength = cellA.length;
			var i:int = 0;
			for(i = 0; i < cellALength - 1; i++)	{
				colObj01 = cellA[i];
				for(var j:int = i + 1; j < cellALength; j++)	{
					colObj02 =  cellA[j];
					_checks.push(colObj01, colObj02);
				}
			}
		}
		
		private function checkTwoCells(x1:int, y1:int, x2:int, y2:int):void {
			if (x2 >= _numCols || x2 < 0 || y2 >= _numRows || y2 < 0) { return; }
			
			cellA = _grid[y1 * _numCols + x1];
			cellB = _grid[y2 * _numCols + x2];
			if (cellA == null || cellB == null) { return; }
			
			cellALength = cellA.length;
			cellBLength = cellB.length;
			var i:int = 0;
			for(i = 0; i < cellALength; i++) {
				colObj01 = cellA[i];
				for(var j:int = 0; j < cellBLength; j++) {
					colObj02 = cellB[j];
					_checks.push(colObj01, colObj02);
				}
			}
		}
		
		protected function get rangeHitTestBD():Boolean {
			tempPoint.x = colObj02.x;
			tempPoint.y = colObj02.y;
			if (colObj01.hitTestBitmapData(colObj02.bitmapData, tempPoint ) ) {
				return true;
			} else {
				return false;
			}
		}
		
		public function get pause():Boolean {
			return _pause;
		}
		
		public function set pause(bool:Boolean):void {
			_pause = bool;
		}
		
		public function collide():void {
			if (_pause) { return; }
			if (vColisionList.length < 1) { return; }
			var i:int;
			var tempbool:Boolean;	
			
			i = vColisionList.length -1;
			//trace("vColisionList " + vColisionList );
			_checks.length = 0;
			_grid = new Vector.<Vector.<IColisionable>> (_numCells);
			
			//check || colisionGrid
			for (i; i > -1; i--) {
				colObj01 = vColisionList[i];
				dy = colObj01.y / _gridSize;
				dx = colObj01.x / _gridSize;
				
				y2 = (dy ^ (dy >> 31)) - (dy >> 31);
				x2 = (dx ^ (dx >> 31)) - (dx >> 31);
				
				index2 = x2 * y2;
				index1 = Math.abs(dy) * _numCols + Math.abs(dx);
				
				//trace("index1 " + index1 + "|| numcels " + _numCells);
				if (index1 < _numCells) {
					if (_grid[index1] == null) {
						_grid[index1] = new Vector.<IColisionable>;
					}	
					_grid[index1].push(colObj01);
				}
			}
			//check grid || colisionGrid
			i = 0;
			for(i; i < _numCols; i++) {
				for(var j:int = 0; j < _numRows; j++) {
					checkOneCell(i,	j);
					checkTwoCells(i, j, i + 1, j);
					checkTwoCells(i, j, i - 1, j + 1);
					checkTwoCells(i, j, i, j + 1);
					checkTwoCells(i, j, i + 1, j + 1);
				}
			}
			
			i = _checks.length - 1;
			for (i; i > 0; i--) {
				colObj01 = _checks[i];
				colObj02 = _checks[i - 1];
				if (_customColision) {
					customColision();
				} else {
					if (!bitmapDataColisionTest) {
						dx = colObj01.x - colObj02.x;
						dy = colObj01.y - colObj02.y;
						dist = Math.sqrt((dx * dx) + (dy * dy));
						if (dist < _minHitBox) {
							tempbool = colObj01.collide(colObj02);
							if (tempbool) { colObj02.collide(colObj01); }
						}
					} else {
						tempPoint.x = colObj01.x;
						tempPoint.y = colObj02.y;
						if (colObj01.hitTestBitmapData(colObj02.bitmapData, tempPoint ) ) {
							tempbool = colObj01.collide(colObj02);
							if (tempbool) { colObj02.collide(colObj01); }
						}
					}
				}
			}			
		}
		
		protected function customColision():void {
			
		}
		
		public function dispose():void {
			_checks = null;
			vColisionList = null;
			
			
			colObj01 = null;
			colObj02 = null;
			id01 = null;
			id02 = null;
			tempPoint = null;
		
		}
		
	}
}